package com.cherrypt.api.core.controller;

import com.cherrypt.api.common.constant.WebConst;
import com.cherrypt.api.common.enums.DiscountEnum;
import com.cherrypt.api.common.enums.PostInfoTypeEnum;
import com.cherrypt.api.common.enums.ResultEnum;
import com.cherrypt.api.common.exception.TipException;
import com.cherrypt.api.core.service.*;
import com.cherrypt.api.core.util.*;
import com.cherrypt.api.model.bo.PostBo;
import com.cherrypt.api.model.bo.ReleaseBo;
import com.cherrypt.api.model.bo.SeriesBo;
import com.cherrypt.api.model.bo.TorrentBo;
import com.cherrypt.api.model.dataobject.*;
import com.cherrypt.api.model.dto.*;
import com.cherrypt.api.model.vo.GetPostListVo;
import com.cherrypt.api.model.vo.ReleaseVo;
import com.cherrypt.api.model.vo.SeriesVo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author intent
 * @date 2019/8/2 15:56
 * @about <link href='http://zzyitj.xyz/'/>
 */
@Api(tags = "发布文字、获取文章数、获取文章、根据imdbId或豆瓣id检索信息")
@RestController
@RequestMapping(value = "${api.url.post.value}")
public class PostController {
    private static final Logger logger = LoggerFactory.getLogger(PostController.class);

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private PostService postService;

    @Autowired
    private PostInfoService postInfoService;

    @Autowired
    private SeriesService seriesService;

    @Autowired
    private SeriesTorrentService seriesTorrentService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private DiscountService discountService;

    @Autowired
    private PeerService peerService;

    @Value(value = "${api.config.post.passWaitLen}")
    private int passWaitLen;

    @ApiOperation(value = "发布文章")
    @ApiImplicitParam(name = "token", value = "Header携带token辨识用户", example = WebConst.TOKEN,
            dataType = "string", paramType = "header", required = true)
    @PostMapping(value = "")
    public ResponseEntity release(@RequestBody ReleaseVo releaseVo,
                                  @ApiIgnore HttpServletRequest request)
            throws JsonProcessingException, TipException {
        int userId = JwtUtils.getUserIdFromRequest(request);
        ReleaseQualityDto releaseQualityDto = ReleaseBo.getPostQualityByReleaseVo(releaseVo);
        String qualityJson = JackJsonUtils.json(releaseQualityDto);
        Post post = ReleaseBo.getPostByReleaseVo(releaseVo, userId, qualityJson);
        // 保存文章
        if (postService.save(post)) {
            boolean save = true;
            // 保存所有系列
            for (SeriesVo seriesVo : releaseVo.getSeries()) {
                Series series = new Series();
                series.setName(seriesVo.getName());
                series.setRemarks(seriesVo.getRemarks());
                series.setUid(userId);
                series.setPid(post.getId());
                if (seriesService.save(series)) {
                    for (int tid : seriesVo.getTorrents()) {
                        // 保存系列种子关系
                        SeriesTorrent seriesTorrent = new SeriesTorrent();
                        seriesTorrent.setSid(series.getId());
                        seriesTorrent.setTid(tid);
                        if (!seriesTorrentService.save(seriesTorrent)) {
                            save = false;
                        }
                    }
                } else {
                    // 系列保存失败直接抛出异常
                    throw new TipException(ResultEnum.RELEASE_SERIES_ERROR);
                }
            }
            if (save) {
                if (postService.getCountByUid(userId) > passWaitLen) {
                    postService.updateWait(post.getId(), false);
                }
            } else {
                throw new TipException(ResultEnum.RELEASE_TORRENT_ERROR);
            }
            logger.info("用户: {}, 发布了新文章, id: {}, 标题: {}", userId, post.getId(), post.getTitle());
            return ResponseEntity.ok().build();
        }
        throw new TipException(ResultEnum.RELEASE_ERROR);
    }

    @ApiOperation(value = "获取文章数")
    @ApiImplicitParam(name = "token", value = "Header携带token辨识用户", example = WebConst.TOKEN,
            dataType = "string", paramType = "header", required = true)
    @GetMapping(value = "${api.url.post.count}")
    public ResponseEntity<String> count(@PathVariable("wait") Boolean wait) {
        return ResponseEntity.ok(JackJsonUtils.json("count", this.postService.getCount(wait)));
    }

    @ApiOperation(value = "根据分类名获取文章数")
    @ApiImplicitParam(name = "token", value = "Header携带token辨识用户", example = WebConst.TOKEN,
            dataType = "string", paramType = "header", required = true)
    @GetMapping(value = "${api.url.post.countByCategoryName}")
    public ResponseEntity<String> countByCategoryName(@PathVariable("categoryName") String categoryName,
                                                      @PathVariable("wait") Boolean wait) {
        if ("all".equals(categoryName)) {
            return count(wait);
        }
        return ResponseEntity.ok(JackJsonUtils.json("count", this.postService.getCountByCategoryName(categoryName, wait)));
    }

    @ApiOperation(value = "根据文章ID获取文章")
    @ApiImplicitParam(name = "token", value = "Header携带token辨识用户", example = WebConst.TOKEN,
            dataType = "string", paramType = "header", required = true)
    @GetMapping(value = "${api.url.post.id}")
    public ResponseEntity<PostDto> getPost(@PathVariable("id") Integer id) throws TipException, IOException {
        Post post = postService.getById(id);
        if (post != null) {
            List<SeriesDto> seriesDtoList = new ArrayList<>();
            // 获取文章对应的所有系列
            List<Series> seriesList = seriesService.getSeriesList(post.getId());
            seriesList.forEach(series -> {
                // 获取系列下所有种子
                List<Torrent> torrentList = seriesTorrentService.getTorrentListBySid(series.getId());
                Map<Integer, Discount> discountMap = new HashMap<>();
                Map<Integer, Integer> uploadMap = new HashMap<>();
                Map<Integer, Integer> downloadMap = new HashMap<>();
                Map<Integer, Integer> completeMap = new HashMap<>();
                // 获取种子的优惠信息等
                torrentList.forEach(torrent -> {
                    uploadMap.put(torrent.getId(), peerService.getPeerLen(torrent.getId()));
                    downloadMap.put(torrent.getId(), peerService.getDownloadPeerLen(torrent.getId()));
                    completeMap.put(torrent.getId(), peerService.getComplete(torrent.getId()));
                    Discount discount = discountService.getById(torrent.getDiscountId());
                    if (discount.getLimitTime() == 0
                            || discount.getCreated() + discount.getLimitTime() > System.currentTimeMillis()) {
                        discountMap.put(torrent.getId(), discount);
                    } else {
                        discountMap.put(torrent.getId(), DiscountEnum.toDiscount(DiscountEnum.NORMAL));
                    }
                });
                seriesDtoList.add(SeriesBo.getSeriesDto(
                        series, TorrentBo.getTorrentDtoList(torrentList, discountMap, uploadMap, completeMap, downloadMap)));
            });
            return ResponseEntity.ok(PostBo.getPostDto(post,
                    userInfoService.getUserInfoByUid(post.getUid()).getUsername(),
                    categoryService.getCategory(post.getCategoryId()).getName(),
                    seriesDtoList,
                    postInfoService.getByDoubanId(post.getDoubanId()),
                    postInfoService.getByImdbId(post.getImdbId())));
        }
        throw new TipException(ResultEnum.POST_EMPTY_ERROR);
    }

    @ApiOperation(value = "用户获取所有文章")
    @ApiImplicitParam(name = "token", value = "Header携带token辨识用户", example = WebConst.TOKEN,
            dataType = "string", paramType = "header", required = true)
    @PostMapping(value = "${api.url.post.list}")
    public ResponseEntity<List<PostDto>> list(@RequestBody GetPostListVo postListVo,
                                              @ApiIgnore HttpServletRequest request) throws TipException, IOException {
        if (postListVo.getWait() == null) {
            throw new TipException(ResultEnum.ARGS_ERROR);
        }
        if (postListVo.getPageSize() == null || postListVo.getPageSize() > 50) {
            throw new TipException(ResultEnum.GET_POST_LEN_ERROR);
        }
        List<Post> postList;
        // 返回候选
        if (StringUtils.isBlank(postListVo.getCategoryName()) || postListVo.getWait()) {
            // 如果没有分类名,说明没有排序
            if (StringUtils.isBlank(postListVo.getSortKey())) {
                postList = postService.getPostPage(
                        postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getWait()).getRecords();
            } else {
                switch (postListVo.getSortKey()) {
                    case "created":
                        postList = postService.getPostPageOrderByCreated(
                                postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getDesc(), postListVo.getWait()).getRecords();
                        break;
                    case "title":
                        postList = postService.getPostPageOrderByTitle(
                                postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getDesc(), postListVo.getWait()).getRecords();
                        break;
                    case "uid":
                        postList = postService.getPostPageOrderByUid(
                                postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getDesc(), postListVo.getWait()).getRecords();
                        break;
                    case "size":
                        postList = postService.getPostPageOrderBySize(
                                postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getDesc(), postListVo.getWait()).getRecords();
                        break;
                    default:
                        throw new TipException(ResultEnum.CATEGORY_ERROR);
                }
            }
        } else {
            if (StringUtils.isBlank(postListVo.getSortKey())) {
                postList = postService.getPostPageByCategoryName(
                        postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getCategoryName(), false).getRecords();
            } else {
                switch (postListVo.getSortKey()) {
                    case "created":
                        postList = postService.getPostPageByCategoryNameOrderByCreated(
                                postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getCategoryName(), postListVo.getDesc(), false).getRecords();
                        break;
                    case "title":
                        postList = postService.getPostPageByCategoryNameOrderByTitle(
                                postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getCategoryName(), postListVo.getDesc(), false).getRecords();
                        break;
                    case "uid":
                        postList = postService.getPostPageByCategoryNameOrderByUid(
                                postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getCategoryName(), postListVo.getDesc(), false).getRecords();
                        break;
                    case "size":
                        postList = postService.getPostPageByCategoryNameOrderBySize(
                                postListVo.getPageIndex(), postListVo.getPageSize(), postListVo.getCategoryName(), postListVo.getDesc(), false).getRecords();
                        break;
                    default:
                        throw new TipException(ResultEnum.CATEGORY_ERROR);
                }
            }
        }
        List<PostDto> postDtoList = new ArrayList<>();
        for (Post post : postList) {
            List<SeriesDto> seriesDtoList = new ArrayList<>();
            if (!postListVo.getWait()) {
                // 获取文章对应的所有系列
                List<Series> seriesList = seriesService.getSeriesList(post.getId());
                seriesList.forEach(series -> {
                    // 获取系列下所有种子
                    List<Torrent> torrentList = seriesTorrentService.getTorrentListBySid(series.getId());
                    Map<Integer, Discount> discountMap = new HashMap<>();
                    Map<Integer, Integer> uploadMap = new HashMap<>();
                    Map<Integer, Integer> downloadMap = new HashMap<>();
                    Map<Integer, Integer> completeMap = new HashMap<>();
                    // 获取种子的优惠信息等
                    torrentList.forEach(torrent -> {
                        uploadMap.put(torrent.getId(), peerService.getPeerLen(torrent.getId()));
                        downloadMap.put(torrent.getId(), peerService.getDownloadPeerLen(torrent.getId()));
                        completeMap.put(torrent.getId(), peerService.getComplete(torrent.getId()));
                        Discount discount = discountService.getById(torrent.getDiscountId());
                        if (discount.getLimitTime() == 0
                                || discount.getCreated() + discount.getLimitTime() > System.currentTimeMillis()) {
                            discountMap.put(torrent.getId(), discount);
                        } else {
                            discountMap.put(torrent.getId(), DiscountEnum.toDiscount(DiscountEnum.NORMAL));
                        }
                    });
                    seriesDtoList.add(
                            SeriesBo.getSeriesDto(
                                    series, TorrentBo.getTorrentDtoList(torrentList, discountMap, uploadMap, completeMap, downloadMap)));
                });
            }
            postDtoList.add(
                    PostBo.getPostDto(post,
                            userInfoService.getUserInfoByUid(post.getUid()).getUsername(),
                            categoryService.getCategory(post.getCategoryId()).getName(),
                            seriesDtoList,
                            postInfoService.getSummaryByDoubanId(post.getDoubanId()),
                            postInfoService.getPlotByImdbId(post.getImdbId())));
        }
        return ResponseEntity.ok(postDtoList);
    }

    @ApiOperation(value = "根据imdbId或豆瓣id检索信息")
    @ApiImplicitParam(name = "token", value = "Header携带token辨识用户", example = WebConst.TOKEN,
            dataType = "string", paramType = "header", required = true)
    @GetMapping(value = "${api.url.post.info}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity info(@PathVariable String type, @PathVariable String id,
                               @ApiIgnore HttpServletRequest request) throws TipException {
        if (StringUtils.isNotBlank(id)) {
            // imdb
            if (PostInfoTypeEnum.IMDB.getType().equals(type)) {
                long imdbId = Long.valueOf(id);
                // 先检查数据库中有没有
                ImdbDto imdb = postInfoService.getImdbByImdbId(imdbId);
                if (imdb != null) {
                    return ResponseEntity.ok(imdb);
                } else {
                    try {
                        JsonNode jsonNode = ImdbUtils.getImdbById(imdbId)
                                .doOnError(throwable -> logger.error("获取imdb信息错误, imdbId: {}", imdbId))
                                .block();
                        if (jsonNode != null) {
                            imdb = ImdbUtils.getImdb(jsonNode);
                            // 存储到数据库
                            postInfoService.save(PostInfoUtils.getPostInfoByImdb(jsonNode));
                            return ResponseEntity.ok(imdb);
                        }
                    } catch (Exception e) {
                        throw new TipException(ResultEnum.IMDB_NOT_FOUND);
                    }
                }
            } else if (PostInfoTypeEnum.DOUBAN_MOVIE.getType().equals(type)
                    || PostInfoTypeEnum.DOUBAN_MUSIC.getType().equals(type)
                    || PostInfoTypeEnum.DOUBAN_BOOK.getType().equals(type)) {
                // 获取doubanId
                long doubanId = Long.valueOf(id);
                DoubanDto doubanDto = null;
                // 根据页面发过来的类型搜索数据库redis或数据库中的数据
                if (PostInfoTypeEnum.DOUBAN_MOVIE.getType().equals(type)) {
                    doubanDto = postInfoService.getDoubanByDoubanId(doubanId, PostInfoTypeEnum.DOUBAN_MOVIE);
                } else if (PostInfoTypeEnum.DOUBAN_MUSIC.getType().equals(type)) {
                    doubanDto = postInfoService.getDoubanByDoubanId(doubanId, PostInfoTypeEnum.DOUBAN_MUSIC);
                } else if (PostInfoTypeEnum.DOUBAN_BOOK.getType().equals(type)) {
                    doubanDto = postInfoService.getDoubanByDoubanId(doubanId, PostInfoTypeEnum.DOUBAN_BOOK);
                }
                // 如果搜索到数据库中存在该数据直接返回
                if (doubanDto != null) {
                    return ResponseEntity.ok(doubanDto);
                } else {
                    // 数据库中没有该条数据，通过api请求获取数据并且保存在数据库中
                    try {
                        Mono<JsonNode> jsonNode = null;
                        JsonNode rootNode;
                        // 判断类型调用不同的api
                        if (PostInfoTypeEnum.DOUBAN_MOVIE.getType().equals(type)) {
                            jsonNode = DoubanUtils.getDoubanMovieById(doubanId);
                        } else if (PostInfoTypeEnum.DOUBAN_MUSIC.getType().equals(type)) {
                            jsonNode = DoubanUtils.getDoubanMusicById(doubanId);
                        } else if (PostInfoTypeEnum.DOUBAN_BOOK.getType().equals(type)) {
                            jsonNode = DoubanUtils.getDoubanBookById(doubanId);
                        }
                        if (jsonNode != null) {
                            rootNode = jsonNode.doOnError(throwable -> logger.error("获取douban信息错误, doubanId: {}", doubanId))
                                    .block();
                            // 如果搜索通过api搜索到
                            if (rootNode != null) {
                                // 判断类型保存在数据库中
                                if (PostInfoTypeEnum.DOUBAN_MOVIE.getType().equals(type)) {
                                    postInfoService.save(PostInfoUtils.getPostInfoByDouban(rootNode, PostInfoTypeEnum.DOUBAN_MOVIE));
                                } else if (PostInfoTypeEnum.DOUBAN_MUSIC.getType().equals(type)) {
                                    postInfoService.save(PostInfoUtils.getPostInfoByDouban(rootNode, PostInfoTypeEnum.DOUBAN_MUSIC));
                                } else if (PostInfoTypeEnum.DOUBAN_BOOK.getType().equals(type)) {
                                    postInfoService.save(PostInfoUtils.getPostInfoByDouban(rootNode, PostInfoTypeEnum.DOUBAN_BOOK));
                                }
                                doubanDto = DoubanUtils.getDouban(rootNode);
                                return ResponseEntity.ok(doubanDto);
                            }
                        }
                    } catch (Exception e) {
                        throw new TipException(ResultEnum.DOUBAN_NOT_FOUND);
                    }
                }
            } else {
                throw new TipException(ResultEnum.METHOD_ERROR);
            }
        }
        throw new TipException(ResultEnum.ARGS_ERROR);
    }
}
